﻿namespace EasyToolkit.Excel.BindingTemplates
{
    using System;
    using EasyToolkit.BindingTemplates.Definitions;
    using EasyToolkit.BindingTemplates.Views;
    using EasyToolkit.Excel.Application;
    using EasyToolkit.Excel.BindingTemplates.Definitions;
    using EasyToolkit.Excel.BindingTemplates.Renderer;
    using EasyToolkit.Tools.Log;
    using Microsoft.Office.Interop.Excel;

    class ExcelTemplateView : RootTemplateView, IExcelTemplateView
    {
        #region attributes and properties
        private ILogger log = Logger.Instance;

        public bool AutoFit
        { get; set; }

        public Worksheet SheetDestination
        { get; private set; }

        public Range FirstOutputCell
        { get; private set; }

        public ExcelRenderer Renderer
        { get; private set; }

        public Action<IExcelTemplateView> OnActivateView
        { get; private set; }

        public Action<IExcelTemplateView> OnRangeChanged
        { get; private set; }

        public Range ClearingCell
        { get; private set; }

        public RenderingArea RenderingArea
        { get { return Renderer == null ? null : RenderingArea.CreateInstance(Renderer.OutputRange); } }
        #endregion

        #region .ctors
        public ExcelTemplateView(ITemplateDefinition templateDefinition, Worksheet sheetDestination, Range firstOutputCell, Range clearingCell)
                                : base(templateDefinition)
        {
            SheetDestination = sheetDestination;
            FirstOutputCell = firstOutputCell;
            ClearingCell = clearingCell;

            AutoFit = true;

            ExcelTemplateDefinition definition = templateDefinition as ExcelTemplateDefinition;
        }
        #endregion

        #region public methods
        /// <summary> Clear the execution previous rendering.</summary>
        public override void Clear()
        {
            lock (syncRoot)
            {
                base.Clear();
                if (!IsDisposed && Renderer != null)
                {
                    Renderer.Clear();
                    if (log.GetLogLevel() == LogType.Debug)
                        log.LogFormat(LogType.Debug, "Sheet '{0}', View '{1}' from '{2}' cleared.", SheetDestination.Name, this.Ident, TemplateDefinition.Name);
                }
            }
        }

        /// <summary>
        /// Bind the template to Excel => Refresh Excel cell from the datasource currently injected. 
        /// </summary>
        public override void Render()
        {
            lock (syncRoot)
            {
                if (!IsDisposed && Renderer != null)
                {
                    try
                    {
                        using (FreezeExcel freezeExcel = new FreezeExcel(FirstOutputCell.Application))
                        {
                            // Clear the previous rendering.
                            ////////////////////////////////
                            Renderer.Clear();

                            // If we haven't got any data source at this point => leave.
                            if (BindingContext != null && BindingContext.ElementsToRender != null)
                            {
                                Renderer.Render();
                                if (log.GetLogLevel() == LogType.Debug)
                                    log.LogFormat(LogType.Debug, "Sheet '{0}', View '{1}' from '{2}' rendered.", SheetDestination.Name, this.Ident, TemplateDefinition.Name);
                                ExecuteAutoFit();
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        string message = string.Format("Sheet '{0}', View '{1}' from '{2}' render failed.", SheetDestination.Name, this.Ident, TemplateDefinition.Name);
                        throw new EasyToolkitException(message, ex, false);
                    }
                }
            }
        }

        /// <summary>
        /// Bind the template to Excel => Render Excel cell based on the datasource currently injected. 
        /// </summary>
        public override void RenderDataOnly()
        {
            if (! IsDisposed && Renderer != null)
            {
                lock (syncRoot)
                {
                    try
                    {
                        if (Renderer.OutputRange == null)
                            Render();
                        else
                        {
                            using (FreezeExcel freezeExcel = new FreezeExcel(FirstOutputCell.Application))
                            {
                                if (BindingContext != null && BindingContext.ElementsToRender != null)
                                {
                                    Renderer.RenderDataOnly();
                                    if (log.GetLogLevel() == LogType.Debug)
                                        log.LogFormat(LogType.Debug, "Sheet '{0}', View '{1}' from '{2}' render data only failed.", SheetDestination.Name, this.Ident, TemplateDefinition.Name);
                                    if (AutoFit)
                                        ExecuteAutoFit();
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        string message = string.Format("Sheet '{0}', View '{1}' from '{2}' render data only failed.", SheetDestination.Name, this.Ident, TemplateDefinition.Name);
                        throw new EasyToolkitException(message, ex, false);
                    }
                }
            }
        }

        override public void SetDataSource(object dataSource)
        {
            if (! IsDisposed)
            {
                lock (syncRoot)
                {
                    try
                    {
                        base.SetDataSource(dataSource);
                       
                        if (Renderer != null)
                            Renderer.Dispose();
                        if (dataSource != null)
                            Renderer = new ExcelRenderer(this);
                    }
                    catch (Exception ex)
                    {
                        string message = string.Format("Sheet '{0}', View '{1}' from '{2}' Set data source failed.", SheetDestination.Name, this.Ident, TemplateDefinition.Name);
                        throw new EasyToolkitException(message, ex, false);
                    }
                }
            }
        }

        public override void Dispose()
        {
            lock (syncRoot)
            {
                if (!IsDisposed)
                {
                    if (Renderer != null)
                    {
                        Renderer.Dispose();
                        Renderer = null;
                    }
                    
                    SheetDestination = null;
                    FirstOutputCell = null;
                    ClearingCell = null;
                    base.Dispose();
                }
            }
        }
        #endregion

        #region private methods
        private void ExecuteAutoFit()
        {
            if (AutoFit && Renderer.OutputRange != null)
            {
                //if (TemplateDefinition.Orientation == Orientation.Horizontal)
                    Renderer.OutputRange.Rows.AutoFit();
                //else
                    Renderer.OutputRange.Columns.AutoFit();
            }
        }
        #endregion
    }
}
